#include "EditorScene.h"
#include "Layouts/UILayout.h"
#include "CCLuaEngine.h"
#include "layers_scenes_transitions_nodes/CCScene.h"
#include "TransformUtils.h"
#include "qevent.h"
#include "qdir.h"
#include "BoxList.h"
#include "qdebug.h"
#include <map>
#include <algorithm>
#include <list>
#include "Command.h"
#include "Selections.h"
#include "StringUtil.h"

enum resize_point {
    RESIZE_POINT_NONE,
    RESIZE_POINT_ALL,
    RESIZE_POINT_CENTER,                // Center, FOR ANCHOR POINT

    RESIZE_POINT_UPPER_LEFT,                // Upper left
    RESIZE_POINT_UPPER_RIGHT,                // Upper right

    RESIZE_POINT_MIDDLE_RIGHT,                // Middle right
    RESIZE_POINT_MIDDLE_LEFT,                // Middle left

    RESIZE_POINT_UPPER_MIDDLE,                // Upper middle
    RESIZE_POINT_BOTTOM_MIDDLE,                // Bottom middle

    RESIZE_POINT_BOTTOM_RIGHT,                // Bottom right
    RESIZE_POINT_BOTTOM_LEFT,                // Bottom left
};
#ifndef min
#define min(a,b) (((a)<(b))?(a):(b))
#endif
#ifndef max
#define max(a,b) (((a)>(b))?(a):(b))
#endif
class BoxNode : public CCNode
{
public:

    static BoxNode* create()
    {
        BoxNode* bn = new BoxNode;
        bn->autorelease();
        return bn;
    }

    void drawWithTarget(Widget* target)
    {
        kmGLPushMatrix();

        kmMat4 transfrom4x4;

        CCAffineTransform tmpAffine = target->nodeToWorldTransform();
        CGAffineToGL(&tmpAffine, transfrom4x4.mat);

        transfrom4x4.mat[14] = m_fVertexZ;

        kmGLMultMatrix(&transfrom4x4);
        drawBox(target);
        kmGLPopMatrix();
    }

    virtual void visit()
    {
        m_drawOrder = ++g_drawOrder;
        if (!m_bVisible) return;

        drawX();
        
        auto set = Selection::getInstance().get();

        for (auto w : set)
            drawWithTarget(w);
    }

    virtual void drawBox(Widget* target)
    {
        const CCSize& size = target->getSize();
        const CCPoint& anchor = target->getAnchorPoint();

        CCPoint origin(-size.width * anchor.x, -size.height * anchor.y);
        CCPoint destination(origin + size);

        ccDrawColor4F(1, 0, 0, 1);
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
        glLineWidth(2);
#endif
        ccDrawRect(origin, destination);

// why ?
#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
        ccPointSize(8);
#else
        glPointSize(16);
#endif
        ccDrawColor4F(0, 1, 0, 1);
        ccDrawPoint(ccp(0, 0));
        ccDrawPoint(origin);
        ccDrawPoint(destination);
        ccDrawPoint(ccp(origin.x, destination.y));
        ccDrawPoint(ccp(destination.x, origin.y));
    }

    CCPoint _xoffset;

    virtual void drawX()
    {
        const CCSize& sz = CCEGLView::sharedOpenGLView()->getFrameSize();

        int h2 = sz.height / 2;
        int w2 = sz.width / 2;
        ccDrawColor4F(1, 1, 1, 1);


        ccDrawLine(ccp(w2 + _xoffset.x, min(0, _xoffset.y)), ccp(w2 + _xoffset.x, max(sz.height, sz.height + _xoffset.y)));
        ccDrawLine(ccp(min(0, _xoffset.x), h2 + _xoffset.y), ccp(max(sz.width, sz.width + _xoffset.x), h2 + _xoffset.y));
    }
};

int tolua_all_open(lua_State* tolua_S);
int luaopen_all_ccs(lua_State* tolua_S);
int luaopen_XXUI(lua_State* tolua_S);

EditorScene::EditorScene(QWidget* parent)
: QCGLWidget(parent)
, _current(nullptr)
, _box(nullptr)
, _root(nullptr)
, _moveRoot(false)
, _mouseState(0)
{

}

void EditorScene::setBGColor(const QColor& c)
{
    _colorbg->setColor(ccc3(c.red(), c.green(), c.blue()));
}

bool EditorScene::init(const QString& scripts_path)
{
	QDir dir;
	CCFileUtils::sharedFileUtils()->addSearchPath(dir.absoluteFilePath("Resources/").toUtf8());//for default res
	CCFileUtils::sharedFileUtils()->addSearchPath(dir.absoluteFilePath("Resources/res/").toUtf8());//for default res

    _scene = CCScene::create();
    auto d = CCDirector::sharedDirector();
    d->runWithScene(_scene);
    d->setDisplayStats(true);
    d->setAnimationInterval(0);

    _root = Widget::create();
    _scene->addChild(_root, 1);

    _colorbg = CCLayerColor::create(ccc4(30, 30, 30, 255));
    _scene->addChild(_colorbg);

    _box = BoxNode::create();
    _scene->addChild(_box, 2);

    auto e = CCLuaEngine::defaultEngine();
    CCScriptEngineManager::sharedManager()->setScriptEngine(e);
    CCLuaStack *pStack = e->getLuaStack();

    tolua_all_open(pStack->getLuaState());
    luaopen_all_ccs(pStack->getLuaState());
    luaopen_XXUI(pStack->getLuaState());
    // 

    pStack->executeString("XXEditor = true");

    pStack->loadChunksFromZIP("framework_precompiled.zip");
	pStack->addSearchPath(dir.absoluteFilePath("Resources/scripts/").toUtf8());
    pStack->addSearchPath(scripts_path.toUtf8());
    pStack->executeString("require 'main'");

    return true;
}

void EditorScene::setCurrent(Widget* w)
{
    Selection::getInstance().selected(w);
}

Widget* EditorScene::getTouched(const CCPoint& pos)
{
    return getTouched(_root, pos);
}

Widget* EditorScene::doSelectedPos(const CCPoint& pos)
{
    _current = getTouched(pos);
    setCurrent(_current);

    return _current;
}

static void moveWidget(Widget* w, const CCPoint& from, const CCPoint& to)
{
    if (w)
    {
        CCPoint f = w->getParent()->convertToNodeSpace(from);
        CCPoint t = w->getParent()->convertToNodeSpace(to);
        w->setPosition(w->getPosition() + t - f);
    }
}

void EditorScene::selectedWidget(Widget* wid)
{
    _current = wid;
    setCurrent(_current);
}

Widget* EditorScene::getTouched(Widget* root, const CCPoint& pos)
{
    if (!root->isVisible()) return nullptr;

    if (!root->getUserObject())
    {
        ccArray* arrayRootChildren = root->getChildren()->data;
        int length = arrayRootChildren->num;
        for (int i = length - 1; i >= 0; i--)
        {
            Widget* touched = getTouched((Widget*)(arrayRootChildren->arr[i]), pos);
            if (touched)
                return touched;
        }
    }
	if (checkTouch(root, pos))
		return root;

    return nullptr;
}

bool EditorScene::checkTouch(Widget* w, const CCPoint& pos)
{
    if (w->isEnabled() && w->isVisible())
    {
        if (w->hitTest(pos) && w->clippingParentAreaContainPoint(pos))
        {
            return true;
        }
    }

    return false;
}

void EditorScene::resizeEvent(QResizeEvent* event)
{
    Super::resizeEvent(event);
    if (_root) 
    {
        _colorbg->setContentSize(CCSize(event->size().width(), event->size().height()));
        _root->setPosition(CCPoint(event->size().width() / 2, event->size().height() / 2));
    }
    if (_box)
        _box->_xoffset = CCPointZero;
}

CCPoint EditorScene::convertToGL(const QPoint& pos)
{
    return CCPoint(pos.x(), size().height() - pos.y());
}

bool is_in(const CCPoint& mouse, const CCPoint& target)
{
    return mouse.getDistance(target) <= 8;
}

int EditorScene::getMouseState(const CCPoint& pos)
{
    const CCPoint& mousepos = _current->convertToNodeSpace(pos);

    if (is_in(mousepos, CCPointZero))
        return RESIZE_POINT_ALL;// RESIZE_POINT_CENTER;

    const CCSize& size = _current->getSize();
    const CCPoint& anchor = _current->getAnchorPoint();

    CCPoint origin(-size.width * anchor.x, -size.height * anchor.y);
    CCPoint destination(origin + size);

    if (is_in(mousepos, origin))
        return RESIZE_POINT_BOTTOM_LEFT;
    else if (is_in(mousepos, destination))
        return RESIZE_POINT_UPPER_RIGHT;
    else if (is_in(mousepos, ccp(origin.x, destination.y)))
        return RESIZE_POINT_UPPER_LEFT;
    else if (is_in(mousepos, ccp(destination.x, origin.y)))
        return RESIZE_POINT_BOTTOM_RIGHT;

    if (checkTouch(_current, pos))
        return RESIZE_POINT_ALL;

    return RESIZE_POINT_NONE;
}

void EditorScene::updateArea(float left, float top, float right, float bottom)
{
    const CCSize& old_size = _current->getSize();
    CCSize new_size(old_size.width - left + right, old_size.height + top - bottom);

    cmd_mgr::getInstance().push(new cmd_Prop(_current, "Size",
        StringUtil::toString<float, float>(new_size.width, new_size.height).c_str()));
    //_current->setSize(new_size);
}

void EditorScene::updateArea(float pixelDeltaX, float pixelDeltaY)
{
    if (!_current) return;
    if (_mouseState == RESIZE_POINT_NONE) return;

    // The mouse is pressed on a resize point; resize each selected window accordingly.
    switch (_mouseState)
    {
    case RESIZE_POINT_UPPER_LEFT:
        // Upper left
        updateArea(pixelDeltaX, pixelDeltaY, 0.0f, 0.0f);
        break;
    case RESIZE_POINT_UPPER_MIDDLE:
        // Upper middle
        updateArea(0.0f, pixelDeltaY, 0.0f, 0.0f);
        break;
    case RESIZE_POINT_UPPER_RIGHT:
        // Upper right
        updateArea(0.0f, pixelDeltaY, pixelDeltaX, 0.0f);
        break;
    case RESIZE_POINT_MIDDLE_RIGHT:
        // Middle right
        updateArea(0.0f, 0.0f, pixelDeltaX, 0.0f);
        break;
    case RESIZE_POINT_BOTTOM_RIGHT:
        // Bottom right
        updateArea(0.0f, 0.0f, pixelDeltaX, pixelDeltaY);
        break;
    case RESIZE_POINT_BOTTOM_MIDDLE:
        // Bottom middle
        updateArea(0.0f, 0.0f, 0.0f, pixelDeltaY);
        break;
    case RESIZE_POINT_BOTTOM_LEFT:
        // Bottom left
        updateArea(pixelDeltaX, 0.0f, 0.0f, pixelDeltaY);
        break;
    case RESIZE_POINT_MIDDLE_LEFT:
        // Middle left
        updateArea(pixelDeltaX, 0.0f, 0.0f, 0.0f);
        break;
    default:
        break;
    }
}

void EditorScene::mousePressEvent(QMouseEvent* event)
{
    _lastPos = convertToGL(event->pos());
    _startPos = _lastPos;

    
    if (_keyPressMap[Qt::Key_Space])
    {
        _moveRoot = true;
        return;
    }
    else if (_keyPressMap[Qt::Key_Control])
    {
        Widget* w = getTouched(_lastPos);
        if (w)
            Selection::getInstance().add(w);
        return;
    }

    if (_mouseState != RESIZE_POINT_NONE && _mouseState != RESIZE_POINT_ALL)
        return;

    if (_mouseState == RESIZE_POINT_ALL)
    {
        dragStart();
        return;
    }

    _moveRoot = false;

    bool in = false;//test touch in selected boxes area.
    auto set = Selection::getInstance().get();
    for (auto w : set)
    {
        if (checkTouch(w, _lastPos))
        {
            in = true;
            break;
        }
    }

    if (!in)
    {
        _current = getTouched(_lastPos);
        setCurrent(_current);
    }
    else
    {
        dragStart();
    }
}

void EditorScene::mouseReleaseEvent(QMouseEvent* event)
{
    if (_dragStart.size())
        dragEnd();

    unsetCursor();
}

void EditorScene::mouseMoveEvent(QMouseEvent* event)
{
    CCPoint curpos = convertToGL(event->pos());
    if (event->buttons() == Qt::NoButton)
    {
        if (Selection::getInstance().getCurrent()) 
        {
            _current = Selection::getInstance().getCurrent();
            _mouseState = getMouseState(curpos);
            if (_mouseState == RESIZE_POINT_BOTTOM_LEFT || _mouseState == RESIZE_POINT_UPPER_RIGHT)
                setCursor(Qt::SizeBDiagCursor);
            else if (_mouseState == RESIZE_POINT_BOTTOM_RIGHT || _mouseState == RESIZE_POINT_UPPER_LEFT)
                setCursor(Qt::SizeFDiagCursor);
            else if (_mouseState == RESIZE_POINT_ALL)
                setCursor(Qt::SizeAllCursor);
            else if (_mouseState == RESIZE_POINT_CENTER)
                setCursor(Qt::SizeAllCursor);
            else 
                unsetCursor();
        }
    }
    else if (event->buttons() == Qt::LeftButton)
    {
        if (_mouseState != RESIZE_POINT_NONE && _mouseState != RESIZE_POINT_ALL)
        {
            updateArea(curpos.x - _lastPos.x, curpos.y - _lastPos.y);
            emit currentPropChange("Size");
        }
        else if (_moveRoot)
        {
            moveWidget(_root, _lastPos, curpos);
            _box->_xoffset = _box->_xoffset + curpos - _lastPos;
        }
        else
        {
            if (!_dragStart.size())
                dragStart();

            for (auto w : Selection::getInstance().get())
            {
                moveWidget(w, _lastPos, curpos);
            }
            if (_dragStart.size() == 1)
                emit currentPropChange("Pos");
        }
    }
    _lastPos = curpos;
}

void EditorScene::keyPressEvent(QKeyEvent *event)
{
    int key = event->key();

    _keyPressMap[key] = key;

    if (!event->isAutoRepeat())
        dragStart();

    if (!(key == Qt::Key_Left || key == Qt::Key_Right || key == Qt::Key_Up || key == Qt::Key_Down))
        return;

    CCPoint pos(CCPointZero);
    if (key == Qt::Key_Left)
        pos.x -= 1;
    else if (key == Qt::Key_Right)
        pos.x += 1;
    else if (key == Qt::Key_Up)
        pos.y += 1;
    else if (key == Qt::Key_Down)
        pos.y -= 1;

    auto set = Selection::getInstance().get();

    for (auto w : set)
    {
        w->setPosition(w->getParent()->convertToNodeSpace(pos + w->convertToWorldSpace(CCPointZero)));
    }

    if (_dragStart.size() == 1)
        emit currentPropChange("Pos");
}

void EditorScene::keyReleaseEvent(QKeyEvent *event)
{
    if (!event->isAutoRepeat())
    {
        int key = event->key();

        auto it = _keyPressMap.find(key);
        if (it != _keyPressMap.end())
            _keyPressMap.erase(it);

        dragEnd();
    }
}

void EditorScene::doSelected(TreeItem* item)
{
    _current = item ? item->getWidget() : nullptr;

    setCurrent(_current);
}

void EditorScene::aliginX()
{
    auto set = Selection::getInstance().get();
    if (set.size() <= 1) return;

    CCPoint total;

    for (auto w : set)
    {
        total = total + w->convertToWorldSpace(CCPointZero);
    }

    total = total / set.size();

    dragStart();

    for (auto w : set)
    {
        w->setPositionX(w->getParent()->convertToNodeSpace(total).x);
    }

    dragEnd();
}

void EditorScene::aliginY()
{
    auto set = Selection::getInstance().get();
    if (set.size() <= 1) return;

    CCPoint total;

    for (auto w : set)
    {
        total = total + w->convertToWorldSpace(CCPointZero);
    }

    total = total / set.size();

    dragStart();

    for (auto w : set)
    {
        w->setPositionY(w->getParent()->convertToNodeSpace(total).y);
    }
    dragEnd();
}

static bool cmp_x_l(Widget* a, Widget* b)
{
    return a->convertToWorldSpace(CCPointZero).x <= b->convertToWorldSpace(CCPointZero).x;
}

void EditorScene::aliginH()
{
    auto set = Selection::getInstance().get();
    if (set.size() <= 2) return;

    float maxi = 0, mini = 999999;
    std::list<Widget*> lst;

    for (auto w : set)
    {
        float x = w->convertToWorldSpace(CCPointZero).x;
        lst.push_back(w);

        maxi = max(x, maxi);
        mini = min(x, mini);
    }

    lst.sort(cmp_x_l);

    float dis = (maxi - mini) / (set.size() - 1);
    CCPoint pos(mini, 0);

    dragStart();

    for (auto it = lst.begin(); it != lst.end(); it++)
    {
        const CCPoint& p = (*it)->getParent()->convertToNodeSpace(pos);
        (*it)->setPositionX(p.x);
        pos.x += dis;
    }

    dragEnd();
}

static bool cmp_y_l(Widget* a, Widget* b)
{
    return a->convertToWorldSpace(CCPointZero).y <= b->convertToWorldSpace(CCPointZero).y;
}

void EditorScene::aliginV()
{
    auto set = Selection::getInstance().get();
    if (set.size() <= 2) return;

    float maxi = 0, mini = 999999;
    std::list<Widget*> lst;

    for (auto w : set)
    {
        float y = w->convertToWorldSpace(CCPointZero).y;
        lst.push_back(w);

        maxi = max(y, maxi);
        mini = min(y, mini);
    }

    lst.sort(cmp_y_l);

    float dis = (maxi - mini) / (set.size() - 1);
    CCPoint pos(0, mini);

    dragStart();

    for (auto it = lst.begin(); it != lst.end(); it++)
    {
        const CCPoint& p = (*it)->getParent()->convertToNodeSpace(pos);
        (*it)->setPositionY(p.y);
        pos.y += dis;
    }

    dragEnd();
}

void EditorScene::zoomIn()
{
    _root->setScale(_root->getScale() + 0.2);
}

void EditorScene::zoomOut()
{
    _root->setScale(_root->getScale() - 0.2);
}

void EditorScene::dragStart()
{
    _dragStart.clear();
    auto set = Selection::getInstance().get();
    for (auto w : set)
    {
        auto& d = (_dragStart[w] = std::pair<CCPoint, CCPoint>());
        d.first = w->getPosition();
    }
}

void EditorScene::dragEnd()
{
    for (auto& it : _dragStart)
    {
        it.second.second = it.first->getPosition();
    }

    cmd_mgr::getInstance().add(new cmd_Drag(_dragStart));
    _dragStart.clear();
}
